#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "../src/index/hash_index.h"
#include "../src/memory/memory_manager.h"
#include "../include/gudb.h"

// Test configuration
#define TEST_KEY_COUNT 1000
#define TEST_BUCKET_COUNT 256

void test_hash_index_basic_operations() {
    printf("Testing hash index basic operations...\n");
    
    // Create memory manager
    memory_manager_t* mm = mm_init(1024 * 1024); // 1MB
    assert(mm != NULL);
    
    // Create hash index
    hash_index_t* index = hash_index_create(mm, TEST_BUCKET_COUNT, 0.75);
    assert(index != NULL);
    
    // Test insert
    const char* key = "test_key";
    const char* value = "test_value";
    
    index_result_t result = hash_index_insert(index, key, strlen(key), 
                                             value, strlen(value));
    assert(result == INDEX_SUCCESS);
    printf("  Insert operation: PASSED\n");
    
    // Test search
    index_search_result_t search_result;
    result = hash_index_search(index, key, strlen(key), &search_result);
    assert(result == INDEX_SUCCESS);
    assert(search_result.found == true);
    assert(search_result.value_size == strlen(value));
    assert(memcmp(search_result.value, value, strlen(value)) == 0);
    printf("  Search operation: PASSED\n");
    
    // Test update
    const char* new_value = "updated_value";
    result = hash_index_update(index, key, strlen(key), 
                              new_value, strlen(new_value));
    assert(result == INDEX_SUCCESS);
    
    // Verify update
    result = hash_index_search(index, key, strlen(key), &search_result);
    assert(result == INDEX_SUCCESS);
    assert(search_result.value_size == strlen(new_value));
    assert(memcmp(search_result.value, new_value, strlen(new_value)) == 0);
    printf("  Update operation: PASSED\n");
    
    // Test delete
    result = hash_index_delete(index, key, strlen(key));
    assert(result == INDEX_SUCCESS);
    
    // Verify deletion
    result = hash_index_search(index, key, strlen(key), &search_result);
    assert(result == INDEX_ERROR_KEY_NOT_FOUND);
    assert(search_result.found == false);
    printf("  Delete operation: PASSED\n");
    
    // Cleanup
    hash_index_destroy(index);
    mm_cleanup(mm);
    
    printf("Hash index basic operations test: PASSED\n\n");
}

void test_hash_index_collisions() {
    printf("Testing hash index collision handling...\n");
    
    memory_manager_t* mm = mm_init(1024 * 1024);
    assert(mm != NULL);
    
    // Create small hash index to force collisions
    hash_index_t* index = hash_index_create(mm, 8, 0.75); // Only 8 buckets
    assert(index != NULL);
    
    // Insert many keys to force collisions
    const int key_count = 32;
    char keys[key_count][32];
    char values[key_count][32];
    
    for (int i = 0; i < key_count; i++) {
        snprintf(keys[i], sizeof(keys[i]), "key_%d", i);
        snprintf(values[i], sizeof(values[i]), "value_%d", i);
        
        index_result_t result = hash_index_insert(index, keys[i], strlen(keys[i]),
                                                 values[i], strlen(values[i]));
        assert(result == INDEX_SUCCESS);
    }
    
    printf("  Inserted %d keys with collisions: PASSED\n", key_count);
    
    // Verify all keys can be found
    for (int i = 0; i < key_count; i++) {
        index_search_result_t search_result;
        index_result_t result = hash_index_search(index, keys[i], strlen(keys[i]), 
                                                 &search_result);
        assert(result == INDEX_SUCCESS);
        assert(search_result.found == true);
        assert(memcmp(search_result.value, values[i], strlen(values[i])) == 0);
    }
    
    printf("  All keys found after collisions: PASSED\n");
    
    // Get statistics
    index_stats_t stats;
    hash_index_get_stats(index, &stats);
    printf("  Load factor: %.2f\n", (double)stats.total_keys / 8);
    printf("  Average chain length: %.2f\n", stats.avg_chain_length);
    printf("  Collisions: %llu\n", (unsigned long long)stats.collisions);
    
    // Cleanup
    hash_index_destroy(index);
    mm_cleanup(mm);
    
    printf("Hash index collision handling test: PASSED\n\n");
}

void test_hash_index_resize() {
    printf("Testing hash index resize functionality...\n");
    
    memory_manager_t* mm = mm_init(2 * 1024 * 1024); // 2MB
    assert(mm != NULL);
    
    // Create hash index with small initial size
    hash_index_t* index = hash_index_create(mm, 16, 0.5); // Low load factor to trigger resize
    assert(index != NULL);
    
    // Insert many keys to trigger resize
    const int key_count = 100;
    char keys[key_count][32];
    char values[key_count][32];
    
    for (int i = 0; i < key_count; i++) {
        snprintf(keys[i], sizeof(keys[i]), "resize_key_%d", i);
        snprintf(values[i], sizeof(values[i]), "resize_value_%d", i);
        
        index_result_t result = hash_index_insert(index, keys[i], strlen(keys[i]),
                                                 values[i], strlen(values[i]));
        assert(result == INDEX_SUCCESS);
    }
    
    printf("  Inserted %d keys (should trigger resize): PASSED\n", key_count);
    
    // Verify all keys are still accessible after resize
    for (int i = 0; i < key_count; i++) {
        index_search_result_t search_result;
        index_result_t result = hash_index_search(index, keys[i], strlen(keys[i]), 
                                                 &search_result);
        assert(result == INDEX_SUCCESS);
        assert(search_result.found == true);
        assert(memcmp(search_result.value, values[i], strlen(values[i])) == 0);
    }
    
    printf("  All keys accessible after resize: PASSED\n");
    
    // Get statistics after resize
    index_stats_t stats;
    hash_index_get_stats(index, &stats);
    printf("  Final key count: %llu\n", (unsigned long long)stats.total_keys);
    printf("  Rehashes performed: %llu\n", (unsigned long long)stats.rehashes);
    
    // Cleanup
    hash_index_destroy(index);
    mm_cleanup(mm);
    
    printf("Hash index resize test: PASSED\n\n");
}

void test_hash_index_performance() {
    printf("Testing hash index performance...\n");
    
    memory_manager_t* mm = mm_init(8 * 1024 * 1024); // 8MB
    assert(mm != NULL);
    
    hash_index_t* index = hash_index_create(mm, 1024, 0.75);
    assert(index != NULL);
    
    const int key_count = 10000;
    char keys[1000][32]; // Reuse key buffer
    char values[1000][64]; // Reuse value buffer
    
    // Time insertion
    clock_t start = clock();
    
    for (int i = 0; i < key_count; i++) {
        int key_idx = i % 1000;
        snprintf(keys[key_idx], sizeof(keys[key_idx]), "perf_key_%d", i);
        snprintf(values[key_idx], sizeof(values[key_idx]), "performance_test_value_%d_with_more_data", i);
        
        index_result_t result = hash_index_insert(index, keys[key_idx], strlen(keys[key_idx]),
                                                 values[key_idx], strlen(values[key_idx]));
        assert(result == INDEX_SUCCESS);
    }
    
    clock_t insert_time = clock() - start;
    printf("  Inserted %d keys in %.2f ms\n", key_count, 
           (double)insert_time / CLOCKS_PER_SEC * 1000);
    
    // Time search
    start = clock();
    
    for (int i = 0; i < key_count; i++) {
        int key_idx = i % 1000;
        snprintf(keys[key_idx], sizeof(keys[key_idx]), "perf_key_%d", i);
        
        index_search_result_t search_result;
        index_result_t result = hash_index_search(index, keys[key_idx], strlen(keys[key_idx]),
                                                 &search_result);
        assert(result == INDEX_SUCCESS);
        assert(search_result.found == true);
    }
    
    clock_t search_time = clock() - start;
    printf("  Searched %d keys in %.2f ms\n", key_count,
           (double)search_time / CLOCKS_PER_SEC * 1000);
    
    // Print statistics
    index_stats_t stats;
    hash_index_get_stats(index, &stats);
    printf("  Final statistics:\n");
    printf("    Total keys: %llu\n", (unsigned long long)stats.total_keys);
    printf("    Total operations: %llu\n", (unsigned long long)stats.total_operations);
    printf("    Collisions: %llu\n", (unsigned long long)stats.collisions);
    printf("    Average chain length: %.2f\n", stats.avg_chain_length);
    printf("    Memory usage: %zu bytes\n", stats.memory_usage);
    
    // Cleanup
    hash_index_destroy(index);
    mm_cleanup(mm);
    
    printf("Hash index performance test: PASSED\n\n");
}

void test_hash_index_validation() {
    printf("Testing hash index validation...\n");
    
    memory_manager_t* mm = mm_init(1024 * 1024);
    assert(mm != NULL);
    
    hash_index_t* index = hash_index_create(mm, 64, 0.75);
    assert(index != NULL);
    
    // Insert some test data
    for (int i = 0; i < 50; i++) {
        char key[32], value[32];
        snprintf(key, sizeof(key), "validate_key_%d", i);
        snprintf(value, sizeof(value), "validate_value_%d", i);
        
        index_result_t result = hash_index_insert(index, key, strlen(key),
                                                 value, strlen(value));
        assert(result == INDEX_SUCCESS);
    }
    
    // Validate index structure
    bool is_valid = hash_index_validate(index);
    assert(is_valid == true);
    printf("  Index validation: PASSED\n");
    
    // Test clear operation
    index_result_t result = hash_index_clear(index);
    assert(result == INDEX_SUCCESS);
    
    // Validate after clear
    is_valid = hash_index_validate(index);
    assert(is_valid == true);
    
    // Verify all keys are gone
    for (int i = 0; i < 50; i++) {
        char key[32];
        snprintf(key, sizeof(key), "validate_key_%d", i);
        
        index_search_result_t search_result;
        result = hash_index_search(index, key, strlen(key), &search_result);
        assert(result == INDEX_ERROR_KEY_NOT_FOUND);
    }
    
    printf("  Clear operation: PASSED\n");
    
    // Cleanup
    hash_index_destroy(index);
    mm_cleanup(mm);
    
    printf("Hash index validation test: PASSED\n\n");
}

int main() {
    printf("=== Hash Index Test Suite ===\n\n");
    
    test_hash_index_basic_operations();
    test_hash_index_collisions();
    test_hash_index_resize();
    test_hash_index_performance();
    test_hash_index_validation();
    
    printf("=== All Hash Index Tests Passed! ===\n");
    
    return 0;
}